#pragma once

#include "utility/sharedptr.h"
#include "utility/utility.h"

namespace Serialisation
{

template <typename Archive, typename T>
void Serialise(Archive& ar, Ut::SharedPtr<T>& t)
{
	if (t)
	{
		ar.Serialise("ObjectID", (uint64) (T*) t);

		if (ar.Register((T*) t))
		{
			const char* typeID = t->GetSerialisationGuid();
			ar.Serialise("TypeID", typeID);

			Handler<Archive>* pHandler = HandlerMap<Archive>::GetHandler(typeID);

			pHandler->Serialise(ar, &*t);
		}
	}
}

template <typename Archive, typename T>
void Serialise(Archive& ar, Ut::WeakPtr<T>& t)
{
	if (t)
	{
		Ut::SharedPtr<T> s(t);

		ar.Serialise("ObjectID", (uint64) (T*) s);

		if (ar.Register((T*) s))
		{
			const char* typeID = s->GetSerialisationGuid();
			ar.Serialise("TypeID", typeID);

			Handler<Archive>* pHandler = HandlerMap<Archive>::GetHandler(typeID);

			pHandler->Serialise(ar, &*s);
		}
	}
}

template <typename Archive, typename T>
void Deserialise(Archive& ar, Ut::SharedPtr<T>& t)
{
	uint64 objectID = 0;
	ar.Serialise("ObjectID", objectID);

	if (objectID != 0)
	{
		Ut::ReferenceCountedPointer* p = ar.Find(objectID);

		if (!p)
		{
			std::string typeID;
			ar.Serialise("TypeID", typeID);

			Handler<typename Archive::Serialiser>* pHandler = HandlerMap<typename Archive::Serialiser>::GetHandler(typeID.c_str());
			void* vp;
			pHandler->DeserialiseObject(ar, vp);
			p = new Ut::ReferenceCountedPointer(vp, pHandler->GetDestructor());
			ar.Register(objectID, p);
		}

		t.SetReference(p);
	}
}

template <typename Archive, typename T>
void Deserialise(Archive& ar, Ut::WeakPtr<T>& t)
{
	uint64 objectID = 0;
	ar.Serialise("ObjectID", objectID);

	if (objectID != 0)
	{
		Ut::ReferenceCountedPointer* p = ar.Find(objectID);

		if (!p)
		{
			std::string typeID;
			ar.Serialise("TypeID", typeID);

			Handler<typename Archive::Serialiser>* pHandler = HandlerMap<typename Archive::Serialiser>::GetHandler(typeID.c_str());
			void* vp;
			pHandler->DeserialiseObject(ar, vp);
			p = new Ut::ReferenceCountedPointer(vp, pHandler->GetDestructor());
			ar.Register(objectID, p);
		}

		t.SetReference(p);
	}
}

template <typename T>
struct SerialisationInfo <std::vector<T>>
{
	const static SerialisationType type = SerialisationType::Array;
};

template <typename Archive, typename T>
void Serialise(Archive& ar, std::vector<T>& t)
{
	for (int i = 0; i < (int) t.size(); i++)
		ar.ArrayElement(t[i]);
}

template <typename Archive, typename T>
void Deserialise(Archive& ar, std::vector<T>& t)
{
	t.clear();
	t.resize(ar.ArraySize());

	for (int i = 0; i < (int) t.size(); i++)
		ar.ArrayElement(i, t[i]);
}

template <typename T>
struct SerialisationInfo <std::list<T>>
{
	const static SerialisationType type = SerialisationType::Array;
};

template <typename Archive, typename T>
void Serialise(Archive& ar, std::list<T>& t)
{
	for (auto& element: t)
		ar.ArrayElement(element);
}

template <typename Archive, typename T>
void Deserialise(Archive& ar, std::list<T>& t)
{
	t.clear();

	for (int i = 0; i < ar.ArraySize(); i++)
	{
		t.emplace_back();
		ar.ArrayElement(i, t.back());
	}
}

template <typename Archive, typename T, typename U>
void Serialise(Archive& ar, std::pair<T, U>& t)
{
	ar.Serialise("First", t.first);
	ar.Serialise("Second", t.second);
}

template <typename Key, typename Value>
struct SerialisationInfo <std::map<Key, Value>>
{
	const static SerialisationType type = SerialisationType::Array;
};

template <typename Archive, typename Key, typename Value>
void Serialise(Archive& ar, std::map<Key, Value>& t)
{
	for (std::map<Key, Value>::iterator i = t.begin(); i != t.end(); i++)
		ar.ArrayElement(*i);
}

template <typename Archive, typename Key, typename Value>
void Deserialise(Archive& ar, std::map<Key, Value>& t)
{
	int count = ar.ArraySize();
	t.clear();

	for (int i = 0; i < count; i++)
	{
		std::pair<Key, Value> element;
		ar.ArrayElement(i, element);
		t.insert(element);
	}
}

template <typename Key, typename Value, typename Hash>
struct SerialisationInfo <std::unordered_map<Key, Value, Hash>>
{
	const static SerialisationType type = SerialisationType::Array;
};

template <typename Archive, typename Key, typename Value, typename Hash>
void Serialise(Archive& ar, std::unordered_map<Key, Value, Hash>& t)
{
	for (auto i = t.begin(); i != t.end(); i++)
		ar.ArrayElement(*i);
}

template <typename Archive, typename Key, typename Value, typename Hash>
void Deserialise(Archive& ar, std::unordered_map<Key, Value, Hash>& t)
{
	int count = ar.ArraySize();
	t.clear();

	for (int i = 0; i < count; i++)
	{
		std::pair<Key, Value> element;
		ar.ArrayElement(i, element);
		t.insert(element);
	}
}

template <typename T>
struct SerialisationInfo <std::set<T>>
{
	const static SerialisationType type = SerialisationType::Array;
};

template <typename Archive, typename T>
void Serialise(Archive& ar, std::set<T>& t)
{
	for (auto i = t.begin(); i != t.end(); i++)
		ar.ArrayElement(const_cast<T&>(*i));
}

template <typename Archive, typename T>
void Deserialise(Archive& ar, std::set<T>& t)
{
	int count = ar.ArraySize();
	t.clear();

	for (int i = 0; i < count; i++)
	{
		T element;
		ar.ArrayElement(i, element);
		t.insert(element);
	}
}

}